/**
 * Copyright 2011 Mathieu Magnaudet
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package edu.enac.geoapp.services;

import java.awt.Color;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.List;

import org.apache.log4j.Logger;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.factory.FactoryRegistryException;
import org.geotools.feature.SchemaException;
import org.geotools.map.DefaultMapLayer;
import org.geotools.map.MapLayer;
import org.geotools.styling.RasterSymbolizer;
import org.geotools.styling.Style;
import org.geotools.styling.StyleBuilder;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.operation.TransformException;

import edu.enac.geoapp.model.DisplayableForecast;
import edu.enac.geoapp.model.DisplayableView;
import edu.enac.geoapp.model.View;

/**
 * Service class that build the various views to be displayed in the MapComponent
 * @author mathieu
 *
 */
public class ViewBuilder {
	private static final ViewBuilder INSTANCE = new ViewBuilder();
	private MapLayer worldLayer;
	private MapLayer airportsLayer;
	private MapLayer flightPlanLayer;
	private MapLayer aircraftLayer;
	private LayerProvider mapLayerBuilder = LayerProvider.getInstance();
	private Logger logger = Logger.getLogger(ViewBuilder.class);
	private DataProvider dataProvider = DataProvider.getInstance();

	private ViewBuilder() {
		try {
			worldLayer = mapLayerBuilder.getWorldLayer(Color.WHITE,
					Color.BLACK, 1f, false);
			worldLayer.setTitle("world");
		} catch (MismatchedDimensionException e) {
			logger.warn("unable to load world map. " + e.getMessage());
		} catch (IOException e) {
			logger.warn("unable to load world map. " + e.getMessage());
		} catch (FactoryException e) {
			logger.warn("unable to load world map. " + e.getMessage());
		} catch (TransformException e) {
			logger.warn("unable to load world map. " + e.getMessage());
		}

		airportsLayer = mapLayerBuilder.getAirportsLayer(Color.BLACK, false);
		airportsLayer.setTitle("airports");
		flightPlanLayer = mapLayerBuilder.getFlightPlanLayer(false);
		flightPlanLayer.setTitle("flight plan");
		try {
			aircraftLayer = AircraftLayerProvider.getInstance().getLayer(false);
			aircraftLayer.setTitle("aircraft");
		} catch (MalformedURLException e) {
			logger.warn("unable to load aircraft map. " + e.getMessage());
		}
	}

	/**
	 * 
	 * @return an instance of {@link ViewBuilder}
	 */
	public static ViewBuilder getInstance() {
		return INSTANCE;
	}

	/**
	 * Builds and returns the specified view 
	 * @param v the type of view
	 * @return the builded view
	 */
	public DisplayableView getView(View v) {
		switch (v) {
		case NAV:
			return buildNav();
		case RADAR_MTO:
			return buildGeotiff(dataProvider.getRadar(), Color.BLACK);
		case SAT_IR:
			return buildGeotiff(dataProvider.getSatIr(), Color.YELLOW);
		case SAT_VIS:
			return buildGeotiff(dataProvider.getSatVis(), Color.YELLOW);
		case TEMSI:
			break;
		case POLYGON:
			return buildPolygon();
		case WINTEM:
			return buildWintem();
		default:
			break;
		}
		return null;
	}

	private DisplayableView buildPolygon() {
		List<DisplayableForecast> forecastList = mapLayerBuilder.getWimsData();
		
		if(forecastList == null) {
			DisplayableView view = new DisplayableView(0, false);
			addAllBackgroundLayers(view);
			return view;
		}
		
		int nbOfDate = forecastList.size();
		DisplayableView view = new DisplayableView(nbOfDate, true);
		addAllBackgroundLayers(view);
		int dateIndex = 0;
		for (DisplayableForecast forecast : forecastList) {
			for (MapLayer layer : forecast.getLayerList()) {
				try {
					String metadata = forecast.getMetadata(layer.getTitle())
							.getTimeParameters().toString();
					view.addData(dateIndex, layer, metadata);
				} catch (IllegalAccessException e) {
					e.printStackTrace();
					System.exit(0);
				}
			}
			dateIndex++;
		}
		return view;
	}

	private void addAllBackgroundLayers(DisplayableView view) {
		view.addBackgroundLayer(worldLayer);
		view.addBackgroundLayer(airportsLayer);
		view.addBackgroundLayer(flightPlanLayer);
		view.addBackgroundLayer(aircraftLayer);
	}

	private DisplayableView buildWintem() {
		MapLayer[][] forecastList = null;
		try {
			forecastList = mapLayerBuilder.getTempData();
		} catch (MismatchedDimensionException e) {
			e.printStackTrace();
			return null;
		} catch (NoSuchAuthorityCodeException e) {
			e.printStackTrace();
			return null;
		} catch (FactoryRegistryException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} catch (FactoryException e) {
			e.printStackTrace();
			return null;
		} catch (TransformException e) {
			e.printStackTrace();
			return null;
		} catch (SchemaException e) {
			e.printStackTrace();
			return null;
		}
		int nbDate = forecastList.length;
		int nbIsoLevel = forecastList[0].length;

		DisplayableView view = new DisplayableView(nbDate, false);
		addAllBackgroundLayers(view);
		for (int date = 0; date < nbDate; date++) {
			for (int isoLevel = 0; isoLevel < nbIsoLevel; isoLevel++) {
				MapLayer layer = forecastList[date][isoLevel];
				try {
					view.addData(date, layer);
				} catch (IllegalAccessException e) {
					e.printStackTrace();
					System.exit(0);
				}
			}
		}
		return view;
	}


	private DisplayableView buildGeotiff(GridCoverage2D rasterFile, Color borderColor) {
		if (rasterFile == null) {
			DisplayableView view = new DisplayableView(0, false);
			addAllBackgroundLayers(view);
			return view;
		}
		DisplayableView view = new DisplayableView(1, true);
		StyleBuilder sb = new StyleBuilder();
		RasterSymbolizer raster = sb.createRasterSymbolizer();

		Style style = sb.createStyle(raster);

		try {
			MapLayer layer = new DefaultMapLayer(rasterFile, style);
			view.addData(0, layer);
			view.addData(0, mapLayerBuilder.getWorldLayer(null, borderColor,
					0.0f, false));
			view.addData(0, mapLayerBuilder.getAirportsLayer(borderColor, false));
			view.addData(0, flightPlanLayer);
			view.addData(0, aircraftLayer);

		} catch (FactoryRegistryException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TransformException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SchemaException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (MismatchedDimensionException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (FactoryException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return view;
	}

	private DisplayableView buildNav() {
		DisplayableView view = new DisplayableView(0, false);
		addAllBackgroundLayers(view);
		return view;
	}
}
